#ifndef PW_PBL_INTFOR_HPP
/// 获取各种条件下对应整形类型的接口文件
#define PW_PBL_INTFOR_HPP

#include "Math.hpp"
#include <cstdint>
#include <limits>
#include <concepts>
#include <algorithm>

namespace pw_pbl::TIntFor
{
	/// 从类型大小（字节）的LOG2值获取能够存储该类型对象对应的整形类型的元计算模板
	template <size_t ByteCntLog2>
	struct Log2Size;

	template <>
	struct Log2Size<0>
	{
		typedef int8_t stype;
		typedef uint8_t utype;
		typedef int_fast8_t sftype;
		typedef uint_fast8_t uftype;
	};
	template <>
	struct Log2Size<1>
	{
		typedef int16_t stype;
		typedef uint16_t utype;
		typedef int_fast16_t sftype;
		typedef uint_fast16_t uftype;
	};
	template <>
	struct Log2Size<2>
	{
		typedef int32_t stype;
		typedef uint32_t utype;
		typedef int_fast32_t sftype;
		typedef uint_fast32_t uftype;
	};
	template <>
	struct Log2Size<3>
	{
		typedef int64_t stype;
		typedef uint64_t utype;
		typedef int_fast64_t sftype;
		typedef uint_fast64_t uftype;
	};

	template <size_t SIZE>
	using Size = Log2Size<Math::Log2Ceil(SIZE)>;

	template <size_t BITCNT>
	using BitCnt = Size<Math::AlignUp(BITCNT, CHAR_BIT) / CHAR_BIT>;

	template <typename T>
	using Type = Size<sizeof(T)>;

	template <auto... V>
	struct _values;

	/// 可存放列出的整数值对应的最小宽度整型
	template <auto... V>
	using Values = typename _values<V...>::type;

} // namespace pw_pbl::TIntFor

namespace pw_pbl::SIntFor
{

	template <size_t ByteCntLog2>
	using Log2Size = typename TIntFor::Log2Size<ByteCntLog2>::stype;

	template <size_t SIZE>
	using Size = typename TIntFor::Size<SIZE>::stype;

	template <size_t BITCNT>
	using BitCnt = typename TIntFor::BitCnt<BITCNT>::stype;

	template <typename T>
	using Type = typename TIntFor::Type<T>::stype;

	template <auto... V>
	using Values = BitCnt<std::max({Math::bit_width(V)...})>;
} // namespace pw_pbl::SIntFor

namespace pw_pbl::UIntFor
{
	template <size_t ByteCntLog2>
	using Log2Size = typename TIntFor::Log2Size<ByteCntLog2>::utype;

	template <size_t SIZE>
	using Size = typename TIntFor::Size<SIZE>::utype;

	template <size_t BITCNT>
	using BitCnt = typename TIntFor::BitCnt<BITCNT>::utype;

	template <typename T>
	using Type = typename TIntFor::Type<T>::utype;

	template <auto... V> requires((V >= 0) && ...)
	using Values = BitCnt<std::max({Math::bit_width(V)...})>;
} // namespace pw_pbl::UIntFor

namespace pw_pbl
{
	/// 指定整数值对应的最小宽度整型值，即保证值不变的前提下缩减存储值所需整型size
	template <auto V>
	inline constexpr auto NarrowIntFor = []() {
		using T = TIntFor::BitCnt<Math::bit_width(V)>;
		if constexpr (std::cmp_less_equal(V, std::numeric_limits<typename T::stype>::max()))
			return typename T::stype{V};
		else
			return typename T::utype{V};
	}();

	template <auto V> requires(V == 0)
	inline constexpr auto NarrowIntFor<V> = int8_t(0);

	/// 指定正整数值对应的最小宽度无符号整型值，即保证值不变的前提下缩减存储值所需整型size
	template <auto V>
	inline constexpr auto NarrowUIntFor = UIntFor::BitCnt<Math::bit_width(V)>{V};

	template <auto V> requires(V == 0)
	inline constexpr auto NarrowUIntFor<V> = uint8_t(0);

	namespace TIntFor
	{
		template <auto... V>
		struct _values
		{
			static constexpr auto _traits = [] {
				bool neg[] = {(V < 0)...};
				size_t bits[] = {Math::bit_width(V)...};
				size_t max = 0;
				bool nf = false, pf = false;
				for (size_t i = 0; i < sizeof...(V); i++)
				{
					nf = nf || neg[i];
					if (bits[i] > max)
						max = bits[i], pf = !neg[i];
					else if (bits[i] == max)
						pf = pf || !neg[i];
				}
				if (nf && pf)
					return std::pair{max + 1, true};
				else if (nf)
					return std::pair{max, nf};
				else if (max > 0b111 && (max & 0b111) == 0 && Math::IsPow2(max >> 3))
					return std::pair{max, false};
				else
					return std::pair{max, true};
			}();
			using type = std::conditional_t<_traits.second, typename BitCnt<_traits.first>::stype, typename BitCnt<_traits.first>::utype>;
		};

		template <auto V>
		struct _values<V>
		{
			using type = std::remove_const_t<decltype(NarrowIntFor<V>)>;
		};
	} // namespace TIntFor
} // namespace pw_pbl

#endif // PW_PBL_INTFOR_HPP
